import inspect
import sqlite3
import akshare as ak
import pandas as pd
from datetime import date, datetime
from typing import Any, Dict, List, Optional, Union, Callable
import logging
import re
import os

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

#全局变量
default_db = "data/origin/akshare_cache.db3"

class Cache:
    """
    Cache类作为akshare调用的中间层，支持动态接口调用和数据库缓存
    使用方式：Cache.stock_us_hist() 代替 akshare.stock_us_hist()
    
    特性：
    - 自动将英文列名转换为中文列名，便于中文用户理解
    - 支持数据库缓存，避免重复API调用
    - 支持动态接口调用，无需手动导入akshare的每个函数
    - 自动处理日期参数和列名标准化
    """
    
    # 英文到中文列名映射表
    _column_mapping = {
        # 日期相关
        'date': '日期',
        'trade_date': '日期',
        'time': '时间',
        'report_date': '报告期',
        'year': '年份',
        'quarter': '季度',
        'month': '月份',
        
        # 股票价格相关
        'open': '开盘价',
        'close': '收盘价',
        'high': '最高价',
        'low': '最低价',
        'prev_close': '前收盘价',
        'pct_change': '涨跌幅',
        'change': '涨跌额',
        'amplitude': '振幅',
        
        # 成交量相关
        'volume': '成交量',
        'amount': '成交额',
        'count': '成交笔数',
        'turnover_rate': '换手率',
        
        # 股票基本信息
        'symbol': '股票代码',
        'name': '股票名称',
        'short_name': '简称',
        'market_cap': '市值',
        'circulating_market_cap': '流通市值',
        'total_shares': '总股本',
        'circulating_shares': '流通股本',
        
        # 财务指标
        'pe_ratio': '市盈率',
        'pb_ratio': '市净率',
        'roe': '净资产收益率',
        'debt_ratio': '资产负债率',
        'revenue': '营业收入',
        'net_profit': '净利润',
        'gross_margin': '毛利率',
        'net_margin': '净利率',
        
        # 宏观经济
        'gdp': 'GDP',
        'cpi': 'CPI',
        'ppi': 'PPI',
        'interest_rate': '利率',
        'exchange_rate': '汇率',
        'inflation_rate': '通胀率',
        
        # 行业分类
        'industry': '行业',
        'sector': '板块',
        'concept': '概念',
        'region': '地区',
        
        # 其他常见字段
        'rank': '排名',
        'value': '数值',
        'ratio': '比例',
        'percentage': '百分比',
        'yoy': '同比',
        'mom': '环比',
        'cumulative': '累计',
        'average': '平均',
        'median': '中位数',
        'std_dev': '标准差',
        'variance': '方差',
        
        # 技术指标
        'ma5': 'MA5',
        'ma10': 'MA10',
        'ma20': 'MA20',
        'ma60': 'MA60',
        'macd': 'MACD',
        'rsi': 'RSI',
        'kdj': 'KDJ',
        'boll': 'BOLL',
        
        # 资金流向
        'main_net_inflow': '主力净流入',
        'super_large_net_inflow': '超大单净流入',
        'large_net_inflow': '大单净流入',
        'medium_net_inflow': '中单净流入',
        'small_net_inflow': '小单净流入',
        
        # 期货相关
        'open_interest': '持仓量',
        'settlement_price': '结算价',
        'limit_up': '涨停价',
        'limit_down': '跌停价',
    }
    
    def __init__(self, db_path: str = default_db, 
                 start_date: date = date(date.today().year, 1, 1), end_date: date = date.today()):
        """
        初始化Cache类
        
        Args:
            db_path: sqlite数据库文件路径
            start_date: 默认起始日期，默认为1月1日
            end_date: 默认结束日期，默认为今天
        """
        self.db_path = db_path
        self.conn = None
        self._init_database()
        
        # 动态属性缓存
        self._dynamic_methods = {}

        self.start_date = start_date
        self.end_date = end_date
        
    def _translate_columns(self, df: pd.DataFrame) -> pd.DataFrame:
        """
        将DataFrame的英文列名转换为中文列名
        
        Args:
            df: 原始DataFrame
            
        Returns:
            列名转换后的DataFrame
        """
        if df.empty:
            return df
            
        # 创建列名映射字典
        rename_dict = {}
        
        for col in df.columns:
            # 直接匹配
            if col.lower() in self._column_mapping:
                rename_dict[col] = self._column_mapping[col.lower()]
        
        # 应用列名转换
        if rename_dict:
            df_translated = df.rename(columns=rename_dict)
            logger.info(f"列名转换完成: {list(rename_dict.keys())} -> {list(rename_dict.values())}")
            return df_translated
        
        return df
    
    def set_date_range(self, start_date: str = None, end_date: str = None):
        """
        设置默认日期范围
        
        Args:
            start_date: 起始日期，格式为YYYYMMDD
            end_date: 结束日期，格式为YYYYMMDD
        """
        if start_date:
            self.start_date : date = start_date
        if end_date:
            self.end_date : date = end_date
        else:
            self.end_date = datetime.now().strftime('%Y%m%d')
        
        logger.info(f"日期范围已设置: {self.start_date} 到 {self.end_date}")
    
    def get_date_range(self) -> Dict[str, str]:
        """
        获取当前日期范围
        
        Returns:
            包含start_date和end_date的字典
        """
        return {
            'start_date': self.start_date,
            'end_date': self.end_date
        }
    
    def _merge_params_with_defaults(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """
        将用户参数与默认日期参数合并
        
        Args:
            params: 用户传入的参数
            
        Returns:
            合并后的参数
        """
        merged_params = params.copy()
        
        # 如果没有指定start_date，使用默认值
        if 'start_date' not in merged_params and self.start_date:
            merged_params['start_date'] = self.start_date
        
        # 如果没有指定end_date，使用默认值
        if 'end_date' not in merged_params and self.end_date:
            merged_params['end_date'] = self.end_date
        
        return merged_params
    
    def _init_database(self):
        """初始化数据库连接"""
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
            
            self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
            self.conn.row_factory = sqlite3.Row
            
            logger.info(f"数据库连接初始化完成: {self.db_path}")
            
        except Exception as e:
            logger.error(f"数据库初始化失败: {e}")
            raise
    
    def _sanitize_api_name(self, api_name: str) -> str:
        """清理API名称，确保符合SQLite表名规范"""
        # 替换特殊字符为下划线
        clean_name = re.sub(r'[^a-zA-Z0-9_]', '_', api_name)
        # 确保以字母开头
        if clean_name[0].isdigit():
            clean_name = 'api_' + clean_name
        return clean_name
    
    def _extract_date_params(self, params: Dict[str, Any]) -> Dict[str, str]:
        """从参数中提取日期相关参数"""
        date_params = {}
        date_keys = ['start_date', 'end_date', 'date', 'period_start', 'period_end']
        
        for key in date_keys:
            if key in params:
                date_params[key] = str(params[key])
        
        return date_params
    
    def _is_table_exists(self, table_name: str) -> bool:
        """检查表是否存在"""
        try:
            cursor = self.conn.cursor()
            cursor.execute("""
                SELECT name FROM sqlite_master 
                WHERE type='table' AND name=?
            """, (table_name,))
            return cursor.fetchone() is not None
        except Exception as e:
            logger.error(f"检查表存在性失败: {e}")
            return False
    
    def _get_cached_data(self, api_name: str, params: Dict[str, Any]) -> Optional[pd.DataFrame]:
        """
        从数据库获取缓存数据，基于日期参数搜索
        
        Args:
            api_name: API名称
            params: 参数
            
        Returns:
            缓存的数据，如果不存在则返回None
        """
        try:
            table_name = self._sanitize_api_name(api_name)
            
            # 检查表是否存在
            if not self._is_table_exists(table_name):
                return None
            
            # 提取日期参数
            date_params = self._extract_date_params(params)
            
            if not date_params:
                # 如果没有日期参数，返回所有数据
                query = f"SELECT * FROM {table_name}"
                result_df = pd.read_sql_query(query, self.conn)
                if not result_df.empty:
                    logger.info(f"从缓存获取数据: {api_name} (全部数据)")
                    return result_df
                return None
            
            # 构建查询条件
            where_conditions = []
            query_params = []
            
            for key, value in date_params.items():
                if key in ['start_date', 'period_start']:
                    where_conditions.append(f"日期 >= ?")
                    query_params.append(value)
                elif key in ['end_date', 'period_end']:
                    where_conditions.append(f"日期 <= ?")
                    query_params.append(value)
                elif key == 'date':
                    where_conditions.append(f"日期 = ?")
                    query_params.append(value)
            
            if where_conditions:
                query = f"SELECT * FROM {table_name} WHERE {' AND '.join(where_conditions)}"
                result_df = pd.read_sql_query(query, self.conn, params=query_params)
                
                if not result_df.empty:
                    logger.info(f"从缓存获取数据: {api_name} (基于日期参数)")
                    return result_df
            
            return None
            
        except Exception as e:
            logger.error(f"获取缓存数据失败: {e}")
            return None
    
    def _set_cached_data(self, api_name: str, params: Dict[str, Any], 
                        data: pd.DataFrame):
        """
        将数据存入缓存，使用DataFrame的to_sql追加方式
        
        Args:
            api_name: API名称
            params: 参数
            data: 要缓存的数据
        """
        try:
            table_name = self._sanitize_api_name(api_name)
            
            # 使用DataFrame的to_sql方法追加数据
            data.to_sql(
                name=table_name,
                con=self.conn,
                if_exists='append',  # 追加模式
                index=False,
                index_label=['symbol', 'date'],  # 设置联合主键
            )
            
            logger.info(f"数据已缓存到表 {table_name}: {api_name}")
            
        except Exception as e:
            logger.error(f"缓存数据失败: {e}")
            # 如果追加失败，尝试创建表
            try:
                data.to_sql(
                    name=table_name,
                    con=self.conn,
                    if_exists='replace',  # 替换模式
                    index=False,
                    method='multi'
                )
                logger.info(f"创建新表并缓存数据: {table_name}")
            except Exception as e2:
                logger.error(f"创建表失败: {e2}")
    
    def _fetch_new_data(self, api_name: str, params: Dict[str, Any]) -> pd.DataFrame:
        """
        从akshare API获取新数据
        
        Args:
            api_name: API名称
            params: API参数
            
        Returns:
            获取到的数据
        """
        try:
            # 检查akshare中是否存在该API
            if not hasattr(ak, api_name):
                raise AttributeError(f"akshare模块中没有找到API: {api_name}")
            
            api_func = getattr(ak, api_name)

            # 过滤params，只保留函数接受的参数
            func_params = inspect.signature(api_func).parameters
            filtered_params = {k: v for k, v in params.items() if k in func_params}

            result = api_func(**filtered_params)
            
            if not isinstance(result, pd.DataFrame):
                raise ValueError(f"API {api_name} 返回的不是DataFrame类型")
            
            if result.empty:
                logger.warning(f"API {api_name} 返回空数据")
                return result
            
            # 转换列名从英文到中文
            result = self._translate_columns(result)
            
            # 若有时间列，没有日期列则移动时间为日期
            # 若有时间列，没有日期列则移动时间为日期
            if '时间' in result.columns and '日期' not in result.columns:
                result = result.rename(columns={'时间': '日期'})

            # 将date列转换为字符串格式（保持一致性）
            if '日期' in result.columns:
                result['日期'] = pd.to_datetime(result['日期'])
            
            # 检查result是否有symbol列，如果没有且参数中有symbol，则添加symbol列
            if '代码' not in result.columns and 'symbol' in params:
                result['代码'] = params['symbol']
            
            return result
            
        except Exception as e:
            logger.error(f"获取新数据失败: {e}")
            raise

    def _call_akshare_api(self, api_name: str, params: Dict[str, Any] = None, 
                         use_cache: bool = True) -> pd.DataFrame:
        """
        调用akshare API的核心方法
        
        Args:
            api_name: akshare API名称
            params: API参数
            use_cache: 是否使用缓存
            
        Returns:
            API返回的数据
        """
        if params is None:
            params = {}
        
        # 合并默认日期参数
        merged_params = self._merge_params_with_defaults(params)
            
        try:
            # 如果使用缓存，先尝试从缓存获取数据
            if use_cache:
                cached_data = self._get_cached_data(api_name, merged_params)
                if cached_data is not None and not cached_data.empty:
                    logger.info(f"使用缓存数据: {api_name}")
                    return cached_data
            
            # 从API获取新数据
            result = self._fetch_new_data(api_name, merged_params)
            
            # 缓存新数据
            if use_cache and not result.empty:
                self._set_cached_data(api_name, merged_params, result)
            
            return result
            
        except Exception as e:
            logger.error(f"调用akshare API失败: {e}")
            raise
    
    def __getattr__(self, name: str) -> Callable:
        """
        动态属性访问，支持 Cache.stock_us_hist() 格式调用
        
        Args:
            name: 属性名（API名称）
            
        Returns:
            动态生成的API调用方法
        """
        # 如果已经缓存了动态方法，直接返回
        if name in self._dynamic_methods:
            return self._dynamic_methods[name]
        
        # 检查akshare中是否存在该API
        if not hasattr(ak, name):
            raise AttributeError(f"akshare模块中没有找到API: {name}")
        
        # 创建动态方法
        def dynamic_api_call(*args, **kwargs):
            """
            动态API调用方法
            
            Args:
                *args: 位置参数（转换为kwargs）
                **kwargs: 关键字参数
                
            Returns:
                API返回的数据
            """
            # 将位置参数转换为关键字参数（需要根据具体API调整）
            if args:
                # 这里可以根据API名称和参数数量进行智能转换
                # 暂时将args作为额外参数处理
                kwargs['_args'] = args
            
            return self._call_akshare_api(name, kwargs)
        
        # 缓存动态方法
        self._dynamic_methods[name] = dynamic_api_call
        
        return dynamic_api_call
    
    def get_cache_info(self) -> Dict[str, Any]:
        """获取缓存信息"""
        try:
            cursor = self.conn.cursor()
            
            # 获取所有表名
            cursor.execute("""
                SELECT name FROM sqlite_master 
                WHERE type='table' AND name NOT LIKE 'sqlite_%'
            """)
            tables = [row[0] for row in cursor.fetchall()]
            
            total_count = 0
            api_stats = []
            
            for table_name in tables:
                try:
                    # 统计每个表的记录数
                    cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
                    table_count = cursor.fetchone()[0]
                    total_count += table_count
                    
                    # 获取日期范围
                    cursor.execute(f"""
                        SELECT MIN(日期) as min_date, MAX(日期) as max_date 
                        FROM {table_name}
                    """)
                    date_range = cursor.fetchone()
                    
                    api_stats.append({
                        'api_name': table_name,
                        'count': table_count,
                        'min_date': date_range[0] if date_range[0] else 'N/A',
                        'max_date': date_range[1] if date_range[1] else 'N/A'
                    })
                    
                except Exception as e:
                    logger.warning(f"统计表 {table_name} 信息失败: {e}")
            
            return {
                'total_count': total_count,
                'api_stats': api_stats,
                'db_path': self.db_path,
                'tables': tables
            }
            
        except Exception as e:
            logger.error(f"获取缓存信息失败: {e}")
            return {}
    
    def clear_cache(self, api_name: str = None):
        """
        清理缓存
        
        Args:
            api_name: 指定API名称，如果为None则清理所有缓存
        """
        try:
            cursor = self.conn.cursor()
            if api_name:
                table_name = self._sanitize_api_name(api_name)
                cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
                logger.info(f"清理了API {api_name} 的缓存表 {table_name}")
            else:
                # 获取所有非系统表
                cursor.execute("""
                    SELECT name FROM sqlite_master 
                    WHERE type='table' AND name NOT LIKE 'sqlite_%'
                """)
                tables = [row[0] for row in cursor.fetchall()]
                
                for table_name in tables:
                    cursor.execute(f"DROP TABLE IF EXISTS {table_name}")
                
                logger.info(f"清理了所有缓存表: {tables}")
            
            self.conn.commit()
            
        except Exception as e:
            logger.error(f"清理缓存失败: {e}")
    
    def close(self):
        """关闭数据库连接"""
        if self.conn:
            self.conn.close()
            logger.info("数据库连接已关闭")
    
    def __enter__(self):
        """上下文管理器入口"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        self.close()


# 使用示例和测试
if __name__ == "__main__":
    # 使用方式1: 创建带默认日期的Cache实例
    try:
        # 创建Cache实例，设置默认日期范围
        ac = Cache(
            start_date="20240101",
            end_date="20241201"
        )
        
        print(f"默认日期范围: {ac.get_date_range()}")
        
        # 获取美股历史数据（使用默认日期范围）
        stock_data = ac.stock_zh_a_hist(
            symbol="600734",
            period="daily",
            adjust="hfq"
            # 不需要指定start_date和end_date，会使用默认值
        )
        print(f"美股数据形状: {stock_data.shape}")
        
        # 查看缓存信息
        cache_info = ac.get_cache_info()
        print(f"缓存信息: {cache_info}")
        
        ac.close()
        
    except Exception as e:
        print(f"测试失败: {e}")
    
    # 使用方式2: 使用全局cache实例
    try:
        # 设置全局cache的默认日期范围
        cache.set_date_range(start_date="20240101")
        print(f"全局cache日期范围: {cache.get_date_range()}")
        
        # 调用API时会自动使用默认日期
        # stock_data = cache.stock_us_hist(symbol="AAPL")
        
    except Exception as e:
        print(f"全局cache测试失败: {e}")
    
    finally:
        cache.close()

